<template>
  <div
    @mouseenter="hovering = true"
    @mouseleave="hovering = false"
    style="width: 100%"
  >
    <el-input
      v-if="inputDisabled || disabled"
      disabled
      v-model="message"
    />
    <el-input
      v-else
      ref="messageRef"
      v-model="message"
      :size="size"
      :placeholder="messageHolder"
      @click="messageClick"
      @input="messageChange"
    >
      <template #suffix>
        <cc-icon
          v-if="hovering"
          style="cursor: pointer;color: #c0c4cc"
          @click.stop="clearInput"
          iconId="018dca412c4ebd53fd1d"
        />
      </template>
    </el-input>
    <el-popover
      ref="popoverRef"
      width="—"
      placement="bottom-start"
      :fallback-placements="['bottom', 'right', 'left', 'top']"
      :visible-arrow="true"
      :visible="popoverVisible"
      trigger="click"
      :virtual-ref="messageRef"
      virtual-triggering
      @hide="popoverHide"
    >
      <el-scrollbar
        wrap-class="popover-scrollbar"
        wrap-style="min-height:0px;max-height: 400px;min-width: 300px"
        view-style="height:100%;"
        v-click-outside="clickOutside"
        v-loading="loading"
        element-loading-text="数据加载中..."
        :noresize="false"
      >
        <el-row class="tree-row">
          <el-tree
            style="width: 100%"
            ref="tree"
            node-key="id"
            :data="treeData"
            :props="treeProps"
            :default-expanded-keys="expandedData"
            :check-strictly="true"
            :filter-node-method="filterNode"
            :expand-on-click-node="!!typeSelect || leafSelect"
            @node-expand="nodeExpand"
            @node-click="nodeClick"
          ></el-tree>
        </el-row>
      </el-scrollbar>
    </el-popover>
  </div>
</template>

<script setup>
import { ref, watch, onMounted, nextTick } from 'vue';
import { useDisabled, ClickOutside as vClickOutside } from 'element-plus';
import api from '@/http/api'

const emit = defineEmits(['update:modelValue', 'change'])
const props = defineProps({
  modelValue: String,
  placeholder: {
    type: String,
    default: '--请选择--'
  },
  dataUrl: String,
  query: Object,
  leafSelect: {
    type: Boolean,
    default: false,
  },
  typeSelect: {
    type: String,
    default: '',
  },
  disabled: {
    type: Boolean,
    default: false
  },
  size: {
    type: String,
    default: null
  }
});
const initializing = ref(false);
const tree = ref();
const popoverRef = ref();
const popoverVisible = ref(false);
const messageHolder = ref(props.placeholder);
const messageRef = ref();
const message = ref('');
const loading = ref(false);
const selectedId = ref('');
const selectedValue = ref('');
const expandedData = ref([]);
const inputDisabled = useDisabled();
const treeData = ref([]);
const treeProps = ref({
  children: 'children',
  isLeaf: 'leaf',
  label: 'text'
});
const hovering = ref(false);

const clearInput = () => {
  changeValue('', '');
};

const messageClick = () => {
  messageHolder.value = selectedValue.value || props.placeholder;
  popoverVisible.value = true;
  message.value = '';
  tree.value.filter('');
  return false;
};

const messageChange = () => {
  tree.value.filter(message.value);
};

const clickOutside = () => {
  popoverVisible.value = false;
};

const popoverHide = () => {
  if (selectedValue.value !== message.value) {
    message.value = selectedValue.value;
  }
};

const changeValue = (id, value) => {
  initializing.value = true;
  selectedId.value = id;
  selectedValue.value = value;
  message.value = value;
  messageHolder.value = props.placeholder;

  emit('update:modelValue', id);
  emit('change');

  nextTick(() => {
    popoverVisible.value = false;
    initializing.value = false;
  });
};

const filterNode = (value, data) => {
  if (!value) return true;
  return data.text.indexOf(value) >= 0;
};

const nodeExpand = () => {
  setTimeout(() => {
    popoverRef.value && popoverRef.value.popperRef.popperInstanceRef.forceUpdate();
  }, 300);
};

const nodeClick = (data, node) => {
  if (data.disabled === true) {
    changeValue('', '');
    return;
  }

  if (props.typeSelect !== '' && props.typeSelect !== data.type) {
    return;
  }

  if (!props.leafSelect || (props.leafSelect && node.isLeaf)) {
    changeValue(data.id || '', data.text || '');
  }
};

const loadTree = async() => {
  api.post(props.dataUrl, props.query || {}).then(res => {
    treeData.value = res.data.children || [];
    expandedData.value = [];
    if (props.modelValue) {
      expandedData.value.push(props.modelValue);
    }
    calcValue();
  });
};

const calcValue = () => {
  if (treeData.value == null || treeData.value.length <= 0) {
    message.value = selectedId.value;
  } else {
    const result = findData(treeData.value, selectedId.value);
    message.value = result ? result.text : '';
  }
  selectedValue.value = message.value;
};

const findData = (list, id) => {
  if (list === undefined || list == null || id === undefined || id == null) {
    return null;
  }

  for (let i = 0; i < list.length; i++) {
    if (list[i].id && list[i].id !== '0' && list[i].id === id) {
      return list[i];
    }

    if (list[i].children !== undefined && list[i].children != null) {
      const result = findData(list[i].children, id);
      if (result) {
        return result;
      }
    }
  }

  return null;
};

watch(() => props.modelValue, (newVal) => {
  if (initializing.value) return;

  selectedId.value = newVal || '';
  calcValue();
});

watch(() => props.query, () => {
  loadTree();
});

onMounted(() => {
  selectedId.value = props.modelValue || '';
  loadTree();
});
</script>